#!/bin/bash
#
# Copyright (C) 2023 Masatake YAMATO <yamato@redhat.com>
#
# This file is part of util-linux.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
TS_TOPDIR="${0%/*}/../.."
TS_DESC="PING and PINGv6 sockets"

. "$TS_TOPDIR"/functions.sh
ts_init "$*"

ts_skip_nonroot

. "$TS_SELF"/lsfd-functions.bash

ts_check_test_command "$TS_CMD_LSFD"
ts_check_test_command "$TS_HELPER_MKFDS"
ts_check_prog "id"

ts_check_native_byteorder

ts_cd "$TS_OUTDIR"

PID=
FD=3
EXPR=(
    '(TYPE == "PING") and (FD == 3)'
    '(TYPE == "PINGv6") and (FD == 3)'
)
FACTORY=(
    ping
    ping6
)
TYPE=(
    'PING'
    'PINGv6'
)
COLNS=(
    INET
    INET6
)
ID=9999

range_check()
{
    local v=$1
    local min=$2
    local max=$3

    [[ $min -le $v && $v -le $max ]]
    return $?
}

ping_group_range_check()
{
    local g
    local group_min group_max

    if ! read group_min group_max < /proc/sys/net/ipv4/ping_group_range; then
       # We can say nothing. Just allow to go ahead.
       return 0
    fi

    if [[ -z "$group_min" || -z "$group_max" ]]; then
       # We can say nothing. Just allow to go ahead.
       return 0
    fi

    for g in $(id -G); do
       if range_check "$g" "$group_min" "$group_max"; then
          return 0
       fi
    done

    return 1
}

ERRMSG=
for i in 0 1; do
    ERRMSG=$("$TS_HELPER_MKFDS"  -c -q "${FACTORY[$i]}" 3 id=$ID 2>&1)
    ERR="$?"
    if [[ "$ERR" == "$EACCES" ]]; then
	case "$ERRMSG" in
	    *bind*)
		MSG="making ${TYPE[$i]} socket with specifying id is not allowed (blocked by SELinux?)"
		;;
	    *socket*)
		if ! ping_group_range_check; then
		    MSG="the group(s) ($(id -G)) of the process is not in the expected range"
		    MSG+=" [$(cat /proc/sys/net/ipv4/ping_group_range)])"
		else
		    MSG="$ERRMSG"
		fi
		;;
	    *)
		MSG="$ERRMSG"
		;;
	esac
	ts_skip "${MSG}"
    elif [[ "$ERR" != 0 ]]; then
	ts_skip "making ${TYPE[$i]} socket is failed ($ERR: $ERRMSG)"
    fi
done

for i in 0 1; do
    ts_init_subtest "${FACTORY[$i]}"
    {
	coproc MKFDS { "$TS_HELPER_MKFDS" "${FACTORY[$i]}" $FD id=$ID; }
	if read -r -u "${MKFDS[0]}" PID; then
	    ${TS_CMD_LSFD} -n \
			   -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,${COLNS[$i]}.LADDR,${COLNS[$i]}.RADDR,PING.ID \
			   -p "${PID}" -Q "${EXPR[$i]}"
	    echo "ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,${COLNS[$i]}.LADDR,${COLNS[$i]}.RADDR,PING.ID": $?
	    echo DONE >&"${MKFDS[1]}"
	fi
	wait "${MKFDS_PID}"

	coproc MKFDS { "$TS_HELPER_MKFDS" "${FACTORY[$i]}" $FD id=$ID connect=0; }
	if read -r -u "${MKFDS[0]}" PID; then
	    ${TS_CMD_LSFD} -n \
			   -o ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,${COLNS[$i]}.LADDR,${COLNS[$i]}.RADDR,PING.ID \
			   -p "${PID}" -Q "${EXPR[$i]}"
	    echo "ASSOC,TYPE,NAME,SOCK.STATE,SOCK.TYPE,${COLNS[$i]}.LADDR,${COLNS[$i]}.RADDR,PING.ID": $?
	    echo DONE >&"${MKFDS[1]}"
	fi
    } > "$TS_OUTPUT" 2>&1
    wait "${MKFDS_PID}"
    ts_finalize_subtest
done

ts_finalize
